package com.imagecollector.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.imagecollector.common.R;
import com.imagecollector.common.constant.JwtClaimsConstant;
import com.imagecollector.common.constant.MessageConstant;
import com.imagecollector.common.constant.RedisConstant;
import com.imagecollector.common.enums.CodeEnum;
import com.imagecollector.common.exception.BaseException;
import com.imagecollector.common.exception.DataConflictException;
import com.imagecollector.common.exception.LoginFailedException;
import com.imagecollector.common.properties.JwtProperties;
import com.imagecollector.common.properties.WeChatProperties;
import com.imagecollector.common.properties.WeChatPropertiesConstant;
import com.imagecollector.common.utils.HttpClientUtil;
import com.imagecollector.common.utils.JwtUtil;
import com.imagecollector.domain.dto.UserDTO;
import com.imagecollector.domain.dto.UserEditDTO;
import com.imagecollector.domain.dto.UserSearchDTO;
import com.imagecollector.domain.entity.User;
import com.imagecollector.domain.vo.CaptchaVo;
import com.imagecollector.domain.vo.LoginVo;
import com.imagecollector.domain.vo.UserVO;
import com.imagecollector.domain.vo.WXLoginVO;
import com.imagecollector.mapper.UserMapper;
import com.imagecollector.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wrf
 * @since 2024-07-31
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Resource
    private UserMapper userMapper;
    @Autowired
    private WeChatProperties weChatProperties;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private JwtProperties jwtProperties;
    @Override
    public User login(String code) {
        String openid=getOpenid(code);
        /*判断openid是否为空，空的话抛出异常*/
        if (openid==null){
            throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
        }
        /*判断当前用户是否为新用户*/
        User user = lambdaQuery().eq(User::getOpenId, openid).one();
        /*如果是新用户，自动完成注册*/
        if (user == null){
            user = User.builder()
                    .openId(openid)
                    .createTime(LocalDateTime.now())
                    .build();
            /*返回这个用户对象*/
            save(user);
        }

        return user;
    }

    @Override
    public CaptchaVo getCaptcha() {

        SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 4);
        specCaptcha.setCharType(Captcha.TYPE_DEFAULT);
        String code = specCaptcha.text().toLowerCase();
        String key = RedisConstant.ADMIN_LOGIN_PREFIX + UUID.randomUUID();
        String image = specCaptcha.toBase64();
        redisTemplate.opsForValue().set(key, code, RedisConstant.ADMIN_LOGIN_CAPTCHA_TTL_SEC, TimeUnit.SECONDS);
        return new CaptchaVo(image, key);
    }

    @Override
    public WXLoginVO loginAdmin(LoginVo loginVo) {
        //1.判断是否输入了验证码
        if (!StringUtils.hasText(loginVo.getCaptchaCode())) {
            throw new LoginFailedException(CodeEnum.ADMIN_CAPTCHA_CODE_NOT_FOUND.getMsg());
        }

        //2.校验验证码
        String code = redisTemplate.opsForValue().get(loginVo.getCaptchaKey());
        if (code == null) {
            throw new LoginFailedException(CodeEnum.ADMIN_CAPTCHA_CODE_EXPIRED.getMsg());
        }

        if (!code.equals(loginVo.getCaptchaCode().toLowerCase())) {
            throw new LoginFailedException(CodeEnum.ADMIN_CAPTCHA_CODE_ERROR.getMsg());
        }

        //3.校验用户是否存在
        User user = lambdaQuery().eq(User::getPhone, loginVo.getPhone()).one();

        if (user == null) {
            throw new LoginFailedException(CodeEnum.ADMIN_ACCOUNT_NOT_EXIST_ERROR.getMsg());
        }


        //5.校验用户密码,DigestUtils.md5Hex(loginVo.getPassword()),md5加密
        if (!user.getPassword().equals(DigestUtils.md5Hex(loginVo.getPassword()))) {
            throw new LoginFailedException(CodeEnum.ADMIN_ACCOUNT_ERROR.getMsg());
        }

        Map<String, Object> claims = new HashMap<>();
        Integer id = Integer.valueOf(user.getId());

        claims.put(JwtClaimsConstant.ADMIN_ID, id);
        //6.创建并返回TOKEN
        String jwt = JwtUtil.createJWT(jwtProperties.getAdminSecretKey(), jwtProperties.getAdminTtl(), claims);
        return WXLoginVO.builder()
                .token(jwt)
                .id(id)
                .role(user.getRole())
                .nickName(user.getNickName())
                .build();
    }

    /* */
    @Override
    public void saveOrUpdateUser(UserEditDTO userEditDTO) {

        String nikeName = userEditDTO.getNikeName();
        Integer phone = userEditDTO.getPhone();


        lambdaUpdate().eq(User::getId, userEditDTO.getId())
                .set(!nikeName.isEmpty(), User::getNickName, nikeName)
                .set(phone != null, User::getPhone, phone)
                .update();
    }

    /*微信登录接口，拿到openid*/
    private String getOpenid(String code){
        /*调用微信接口服务*/
        Map<String, String> map=new HashMap<>();
        map.put(WeChatPropertiesConstant.APPID,weChatProperties.getAppid());
        map.put(WeChatPropertiesConstant.SECRET,weChatProperties.getSecret());
        map.put(WeChatPropertiesConstant.JS_CODE,code);
        map.put(WeChatPropertiesConstant.GRANT_TYPE,WeChatPropertiesConstant.GRANT_TYPE_VALUE);
        String json= HttpClientUtil.doGet(WeChatPropertiesConstant.WECHATURL,map);

        JSONObject object= JSON.parseObject(json);
        return object.getString("openid");
    }
    @Override
    public R saveOne(UserDTO userDTO) {
        if(userDTO.getRole() < 2 || userDTO.getRole() > 3){
            throw new DataConflictException("新增用户时角色分配异常");
        }
        User user = BeanUtil.toBean(userDTO, User.class);
        int insert = baseMapper.insert(user);
        if(insert < 1){
            throw new BaseException(insert, "用户添加失败");
        }
        return R.success();
    }

    @Override
    public R updateStatus(Integer userId, Integer status) {
        boolean update = lambdaUpdate()
                .eq(User::getId, userId)
                .set(User::getStatus, status)
                .update();
        if(!update){
            throw new BaseException(0, "用户状态更新失败");
        }
        return R.success();
    }

    @Override
    public R updatePassword(Integer phone, String oldPassword, String password) {
        //校验原来密码是否正确
        LambdaQueryWrapper<User> eq = Wrappers.lambdaQuery(User.class)
                .eq(User::getPhone, phone)
                .eq(User::getPassword, oldPassword)
                .eq(User::getStatus, 0);
        User user = baseMapper.selectOne(eq);
        if(user == null){
            return R.fail(CodeEnum.FAIL.getCode(), "用户账号或密码错误");
        }
        boolean update = lambdaUpdate()
                .eq(User::getPhone, phone)
                .set(User::getPassword, password)
                .update();
        if(!update){
            throw new BaseException(0, "用户密码更新失败");
        }
        return R.success();
    }

    @Override
    public R pageQueryUsers(UserSearchDTO requestParam) {
        //获取页面大小
        Integer pageSize = requestParam.getPageSize();
        //获取页号
        Integer pageNo = requestParam.getPageNo();
        //根据页号和页面大小初始化分页对象2
        Page<User> userPage = new Page<>(pageNo, pageSize);
        //可以根据用户id、名称模糊、状态、手机号、地区id构造分页查询条件
        LambdaQueryWrapper<User> userLambdaQueryWrapper = Wrappers.lambdaQuery(User.class)
                .like(requestParam.getId() != null, User::getId, requestParam.getId())
                .like(StrUtil.isNotBlank(requestParam.getNickName()), User::getNickName, requestParam.getNickName())
                .eq(requestParam.getDistirctId() != null, User::getDistirctId, requestParam.getDistirctId())
                .like(requestParam.getPhone() != null, User::getPhone, requestParam.getPhone())
                .eq(requestParam.getStatus() != null, User::getStatus, requestParam.getStatus())
                .orderByDesc(User::getCreateTime);
        //分页查询用户数据
        Page<User> users = baseMapper.selectPage(userPage, userLambdaQueryWrapper);
        if(users == null){
            return R.fail(CodeEnum.FAIL.getCode(), "用户查询失败");
        }
        return R.success().data("users", convertToUserVO(users));
    }

    /**
     * 将用户分页数据转换为用户VO分页数据
     *
     * @param userPage 用户分页数据，包含用户记录以及分页信息
     * @return 转换后的用户VO分页数据
     */
    public Page<UserVO> convertToUserVO(Page<User> userPage) {
        // 获取用户分页数据中的用户记录列表
        List<User> users = userPage.getRecords();
        // 将用户记录列表转换为UserVO列表
        List<UserVO> userVOS = BeanUtil.copyToList(users, UserVO.class);
        // 创建新的UserVO分页对象，并保留原有的分页参数和总数
        Page<UserVO> userVOPage = new Page<>(userPage.getCurrent(), userPage.getSize(), userPage.getTotal());
        // 设置转换后的UserVO列表为分页数据的记录
        userVOPage.setRecords(userVOS);
        // 返回转换后的UserVO分页数据
        return userVOPage;
    }
}

